﻿using System;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Data.Entity.ModelConfiguration;
using System.Data.Entity.ModelConfiguration.Configuration;
using System.Linq;
using System.Reflection;
using Shell.PocoDot.Configurations;

namespace Shell.PocoDot
{
    static class DbModelHelper
    {
        public static DbProviderInfo CreateProviderInfo(ProviderConfiguration pconfig)
        {
            var providerInfo = new DbProviderInfo(pconfig.ProviderInvariantName, pconfig.ProviderManifestToken);
            return providerInfo;
        }

        public static DbModelBuilder CreateModelBuilder()
        {
            return new DbModelBuilder();
        }

        public static void ConfigModelBuilder(DbModelBuilder modelBuilder, ProviderConfiguration pconfig)
        {
            ConfigModelBuilderForPocoDotConfiguration(modelBuilder, pconfig);
            ConfigModelBuilderForRelatedPocoDotConfiguration(modelBuilder, pconfig);
        }

        private static void ConfigModelBuilderForPocoDotConfiguration(DbModelBuilder modelBuilder, ProviderConfiguration pconfig)
        {
            var configType = typeof(EntityTypeConfiguration<>);
            var dconfigs = PocoDotRegistrar.Registrar.Configurations;
            dconfigs = dconfigs.Where(c => c.Provider == pconfig);
            foreach (var dc in dconfigs)
            {
                var currentConfigType = configType.MakeGenericType(dc.PocoDot);
                var currentConfigInstance = currentConfigType.GetConstructor(Type.EmptyTypes).Invoke(null);
                AddConfiguration(modelBuilder, dc.PocoDot, currentConfigInstance);
            }
        }

        private static void ConfigModelBuilderForRelatedPocoDotConfiguration(DbModelBuilder modelBuilder, ProviderConfiguration pconfig)
        {
            var configType = typeof(EntityTypeConfiguration<>);
            var pType = typeof(Relation);
            var currentConfigType = configType.MakeGenericType(pType);
            var currentConfigInstance = currentConfigType.GetConstructor(Type.EmptyTypes).Invoke(null);
            AddConfiguration(modelBuilder, pType, currentConfigInstance);
        }
        
        private static void AddConfiguration(DbModelBuilder modelBuilder, Type persistence, object currentConfigInstance)
        {
            MethodInfo addMethod = null;
            foreach (var m in typeof(ConfigurationRegistrar).GetMethods())
            {
                if (m.Name == "Add" && m.IsGenericMethod)
                {
                    var paras = m.GetParameters();
                    if (paras.Length == 1 && paras[0].ParameterType.Name == typeof(EntityTypeConfiguration<>).Name)
                    {
                        addMethod = m;
                        break;
                    }
                }
            }
            addMethod = addMethod.MakeGenericMethod(persistence);
            addMethod.Invoke(modelBuilder.Configurations, new object[] { currentConfigInstance });
        }
    }
}
